#include <iostream>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>
#include "MemoriaCompartida.h"
#include <Utils.h>
#include <BaseException.h>
#include <Logger.h>
#include <SignalHandler.h>
#include "Playero.h"
#include <ExitHandler.h>
#include <ConfigurationFile.h>

const int SUCCESS_EXIT = 0;

using namespace std;

int main (int argc, char* argv[])
{
	int exitCode = SUCCESS_EXIT;
	bool shouldEnd = false;
	bool soyPadre = true;
	Logger* log = NULL;
	Playero* playero = NULL;
	
	try
	{
		log = Logger::getInstance();
		
		playero = new Playero();
		
		// Event handler para evento de salida
		ExitHandler exitHandler;
		
		// Se registran los event handlers
		SignalHandler::getInstance()->registrarHandler ( SIGQUIT, &exitHandler );
		SignalHandler::getInstance()->registrarHandler ( SIGTERM, &exitHandler );
		
		std::string strPID = Utils::convertIntToStr(getpid());
		log->info("PLAYERO >> Proceso playero iniciado >> Playero PID: " + strPID);
		int cantPlayeros = Utils::convertSrtToInt(ConfigurationFile::getProperty(Properties::CANT_PLAYEROS));
		for(int i = 0 ; i < cantPlayeros -1; i++)
		{
			pid_t pidId = fork();
			
			if(pidId  == 0 )
			{
				std::string strPIDHijo = Utils::convertIntToStr(getpid());
				log->info("PLAYERO >> Proceso playero iniciado >> Playero PID: " + strPIDHijo);
				soyPadre = false;
				break;
			}
		}
		
		log->info("PLAYERO >> Abriendo cola de playeros del jefe");
		playero->joinChiefClientQueue();
		
		log->info("PLAYERO >> Creando receptor de instrucciones");
		playero->createInstructionReceptor();
		
		while(!shouldEnd && !exitHandler.getGracefulQuit())
		{
			log->info("PLAYERO >> Registrando en cola de playeros del jefe");
			playero->checkInAttQueue();
			
			log->info("PLAYERO >> Esperando a cliente u orden de salida");
			std::string instruction = playero->retrieveInstructions();
			
			if(playero->shouldLeave(instruction))
			{
				log->info("PLAYERO >> Finalizar ejecucion");
				shouldEnd = true;
			}
			else if(playero->hasToAssistClient(instruction))
			{
				log->info("PLAYERO >> Cliente asignado >> Atiendo al cliente");
				playero->assistClient();
				log->info("PLAYERO >> Cliente atendido >> Volviendo a empezar");
			}
			else
			{
				throw BaseException("Instruccion no implementada");
			}
		}
	} catch (const BaseException& e) {
		std::cerr << "ERROR FATAL: " << e.what() << std::endl;
		exitCode = e.getCode();
		if(log != NULL)
			log->error(e.what());
	}
	
	if(playero != NULL)
	{
		if(soyPadre)
		{
			if(log != NULL)
				log->info("PLAYERO >> Playero padre: Esperando a que los otros playeros finalicen");
			int estado;
			while( wait( (void*) &estado) > 0);
			
			if(log != NULL)
				log->info("PLAYERO >> Playero padre: Liberando recursos compartidos entre playeros");
			playero->liberarRecursosCompartidos();
		}
		
		delete playero;
	}
	
	// Libero recursos del signal handler
	SignalHandler::destruir ();
	Logger::destroy();
	
	return exitCode;
}